package cn.yj.alipay.service.handler;

import cn.yj.alipay.config.AliPayConfig;
import cn.yj.alipay.emums.AlipayOrderStatus;
import cn.yj.common.emums.PayType;
import cn.yj.common.entity.PayOrder;
import cn.yj.common.entity.PayRefundOrder;
import cn.yj.common.entity.RefundQueryParams;
import cn.yj.common.exception.PayException;
import cn.yj.common.interfaces.AbstractPay;
import cn.yj.common.monitor.AlipayOrderMonitor;
import cn.yj.common.monitor.IPayOrderMonitor;
import cn.yj.common.monitor.WechatOrderMonitor;
import cn.yj.commons.utils.DateTimeUtils;
import cn.yj.commons.utils.StringUtils;
import cn.yj.tools.exception.ServiceException;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundApplyRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundApplyResponse;
import com.google.gson.Gson;
import jdk.nashorn.internal.runtime.regexp.joni.Regex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 永健
 * @since 2021-09-22 16:48
 */
public abstract class AbstractAliPay<T, R> extends AbstractPay<T, R> {

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private static AlipayClient alipayClient;

    private static CertAlipayRequest certParams;

    public Logger logger() {
        return LOGGER;
    }


    protected AlipayClient doPay() {
        return alipayClient;
    }


    @Resource
    protected void setPayOrderMonitor(AlipayOrderMonitor payOrderMonitor) {
        super.setIPayOrderMonitor(payOrderMonitor);
    }

    protected AliPayConfig config() {
        return getApplicationContext().getBean(AliPayConfig.class);
    }

    /**
     * 初始化证书
     */
    @PostConstruct
    private void init() {
        if (alipayClient == null) {
            AliPayConfig config = config();
            String signModel = config.getSignModel();
            try {
                if (StringUtils.isBlank(signModel) || AliPayConfig.CERT.equals(signModel)) {
                    alipayClient = new DefaultAlipayClient(getClientParams(config));
                    return;
                }
                alipayClient = new DefaultAlipayClient(config.getUrl(), config.getAppId(), config.getAppPrivateKey(),
                        "json", config.getCharset(), config.getAlipayPublicKey(), config.getSignType());
            } catch (AlipayApiException e) {
                e.printStackTrace();
                LOGGER.error("支付宝支付初始化异常");
            }
        }
    }


    protected long getExpireTime(String timeOutExpire, String timeOutExpress) {
        if (StringUtils.isBlank(timeOutExpress) || StringUtils.isBlank(timeOutExpress)) {
            // 默认120分钟
            return System.currentTimeMillis() + (2 * 60 * 60 * 1000);
        }

        if (StringUtils.isNotBlank(timeOutExpire)) {
            try {
                Date expireTime = DateTimeUtils.format(timeOutExpire);
                if (expireTime.getTime() < System.currentTimeMillis() + (5 * 60 * 1000)) {
                    // 如果小于5分钟
                    throw new ServiceException("订单超时时间不能小于5分钟");
                }

                // 大于15天
                if (expireTime.getTime() > System.currentTimeMillis() + (15 * 24 * 60 * 60 * 1000)) {
                    throw new ServiceException("订单超时时间最大为15天");
                }

                return expireTime.getTime();
            } catch (ParseException e) {
                throw new ServiceException("支付过期时间参数有误:" + timeOutExpire);
            }
        }

        long expireTime = System.currentTimeMillis() + (2 * 60 * 60 * 1000);
        if (StringUtils.isNotBlank(timeOutExpress)) {
            Pattern p_number = Pattern.compile("\\d+");
            Pattern p_char = Pattern.compile("[a-zA-Z]+");
            Matcher matcher = p_number.matcher(timeOutExpress);
            Matcher matcher1 = p_char.matcher(timeOutExpress);
            List<Integer> integers = new ArrayList<>();
            List<String> chats = new ArrayList<>();
            while (matcher.find()) {
                integers.add(Integer.parseInt(matcher.group()));
            }
            while (matcher1.find()) {
                chats.add(matcher1.group());
            }

            if (integers.size() != 1 || chats.size() != 1 || chats.get(0).length() != 1) {
                throw new ServiceException("支付过期时间参数有误：" + timeOutExpress);
            }

            String chat = chats.get(0);
            int number = integers.get(0);

            if (number <= 0) {
                throw new ServiceException("支付过期时间参数有误,最小为5m：" + timeOutExpress);
            }

            if (!String.valueOf(Math.round(number)).equals(String.valueOf(number))) {
                throw new ServiceException("支付过期时间参数有误,只能取整数：" + timeOutExpress);
            }

            // 用来计算是否大于15天
            int time = 0;
            if (chat.equalsIgnoreCase("m")) {
                // 不能大于15天
                // 15天=time分
                time = 15 * 24 * 60;
                if (number < 5) {
                    throw new ServiceException("支付过期时间参数有误,最小为5m：" + timeOutExpress);
                }
                expireTime = number * 60 * 1000;
            } else if (chat.equalsIgnoreCase("h")) {
                time = 15 * 24;
                expireTime = number * 60 * 60 * 1000;
            } else if (chat.equalsIgnoreCase("d")) {
                time = 15;
                expireTime = number * 24 * 60 * 60 * 1000;
            } else if (chat.equalsIgnoreCase("c")) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(DateTimeUtils.addDays(new Date(), 1));
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                Date exTime = calendar.getTime();
                return exTime.getTime();
            } else {
                throw new ServiceException("支付过期时间参数有误：" + timeOutExpress);
            }
            if (number > time) {
                throw new ServiceException("支付过期时间参数有误,最小为5m最大为15d：" + timeOutExpress);
            }
            expireTime = System.currentTimeMillis() + expireTime;
        }
        return expireTime;
    }


    private CertAlipayRequest getClientParams(AliPayConfig config) {
        certParams = new CertAlipayRequest();
        //您的AppId
        certParams.setAppId(config.getAppId());
        certParams.setServerUrl(config.getUrl());
        //请更换为您的PKCS8格式的应用私钥
        certParams.setPrivateKey(config.getAppPrivateKey());
        //请更换为您使用的字符集编码，推荐采用utf-8
        certParams.setCharset(config.getCharset());
        certParams.setFormat("json");
        certParams.setSignType(config.getSignType());
        //请更换为您的应用公钥证书文件路径
        certParams.setCertPath(config.getAppCertPath());
        //请更换您的支付宝公钥证书文件路径
        certParams.setAlipayPublicCertPath(config.getAlipayCertPath());
        //更换为支付宝根证书文件路径
        certParams.setRootCertPath(config.getAlipayRootCertPath());
        return certParams;
    }


    @Override
    public PayOrder queryOrder(String outTradeNo) {
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel alipayTradeQueryModel = new AlipayTradeQueryModel();
        alipayTradeQueryModel.setOutTradeNo(outTradeNo);
        request.setBizModel(alipayTradeQueryModel);
        PayOrder payOrder = new PayOrder(PayType.ALIPAY, outTradeNo);
        Gson gson = new Gson();
        try {
            AlipayTradeQueryResponse response;
            if (AliPayConfig.CERT.equals(config().getSignModel())) {
                response = doPay().certificateExecute(request);
            } else {
                response = doPay().execute(request);
            }
            payOrder.setTradeStatus(response.getTradeStatus());
            if (response.isSuccess()) {
                payOrder.setSuccess((AlipayOrderStatus.TRADE_SUCCESS.name().equals(response.getTradeStatus())));
                return payOrder;
            }

            // 异常情况
            String body = response.getBody();
            JSONObject jsonObject = gson.fromJson(body, JSONObject.class);
            JSONObject alipay_trade_query_response = jsonObject.getJSONObject("alipay_trade_query_response");
            payOrder.setErrMsg(alipay_trade_query_response.getString("sub_msg"));
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new PayException(e.getErrMsg());
        }
        return payOrder;
    }

    @Override
    public PayRefundOrder queryRefundOrder(RefundQueryParams params) throws PayException {
        AlipayTradeFastpayRefundQueryRequest request =
                new AlipayTradeFastpayRefundQueryRequest();

        AlipayTradeFastpayRefundQueryModel model =
                new AlipayTradeFastpayRefundQueryModel();

        model.setOutTradeNo(params.getOutTradeNo());
        model.setOutRequestNo(params.getOutRefundNo());
        request.setBizModel(model);

        AlipayTradeFastpayRefundQueryResponse response;

        PayRefundOrder payRefundOrder = new PayRefundOrder();
        try {
            if (config().isCert()) {
                response = doPay().certificateExecute(request);
            } else {
                response = doPay().sdkExecute(request);
            }

            String body = response.getBody();
            Gson gson = new Gson();
            JSONObject jsonObject = gson.fromJson(body, JSONObject.class);


            payRefundOrder.setPayType(PayType.ALIPAY);
            payRefundOrder.setOutRefundNo(params.getOutRefundNo());
            if (response.isSuccess()) {
                JSONObject queryResponse = jsonObject.getJSONObject("alipay_trade_fastpay_refund_query_response");
                payRefundOrder.setRefundAmount(new BigDecimal(response.getRefundAmount()));
                payRefundOrder.setStatus(queryResponse.getString("refund_status"));
                if (AlipayOrderStatus.REFUND_SUCCESS.name().equals(queryResponse.getString("refund_status"))) {
                    payRefundOrder.setSuccess(true);
                }
            } else {
                JSONObject subResponse = jsonObject.getJSONObject("alipay_trade_fastpay_refund_query_response");
                String sub_msg = subResponse.getString("sub_msg");
                payRefundOrder.setErrMsg(sub_msg);
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new PayException("查询退款订单异常");
        }
        return payRefundOrder;
    }


    public AlipayTradeRefundApplyResponse refund(AlipayTradeRefundApplyRequest request) throws PayException {

        AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
        if (StringUtils.isBlank(bizModel.getOutTradeNo())) {
            throw new ServiceException("缺少商户单号");
        }

        if (StringUtils.isBlank(bizModel.getOutRequestNo())) {
            throw new ServiceException("缺少商户退款单号");
        }
        String refundAmount = bizModel.getRefundAmount();
        if (StringUtils.isBlank(refundAmount)) {
            throw new ServiceException("缺少商户退款金额");
        }

        AlipayTradeRefundApplyResponse response;

        try {
            if (config().isCert()) {
                response = doPay().certificateExecute(request);
            } else {
                response = doPay().sdkExecute(request);
            }
            if (response.isSuccess()) {
                super.addRefundOrderMonitor(new RefundQueryParams(bizModel.getOutTradeNo(),bizModel.getOutRequestNo()), this);
                return response;
            }
            logger().error("退款失败：sub_code={},sub_msg", response.getSubCode(), response.getSubMsg());
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new PayException("退款失败");
        }
        throw new PayException("退款失败");
    }
}
